﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace ayxjDllClass
{
    //        var isOk = false;
    //        var resultModel = _serialPort.ReadDataCoil(1, 1, ModbusFunctionCode.ReadInputCoil, (ushort)type.GetHashCode());
    //if (resultModel.ResultList != null && resultModel.ResultList.Count > 0)
    //{
    //    isOk = resultModel.ResultList.FirstOrDefault();
    //}
    #region Modbus结果实体
    /// <summary>
    /// Modbus结果实体
    /// </summary>
    /// <typeparam name="DateType"></typeparam>
    public class ModbusResultModel
    {
        public ModbusResultModel()
        {
            IsSucceed = false;
            Msg = "失败(默认)";
        }

        private bool _isSucceed = false;

        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSucceed
        {
            get
            {
                return _isSucceed;
            }
            set
            {
                _isSucceed = value;
                if (IsSucceed)
                {
                    Msg = "成功";
                }
            }
        }

        /// <summary>
        /// 返回消息
        /// </summary>
        public string Msg { get; set; }

        /// <summary>
        /// 发送报文
        /// </summary>
        public string SendDataStr { get; set; }

        /// <summary>
        /// 原始数据
        /// </summary>
        public byte[] Datas { get; set; }
    }

    /// <summary>
    /// Modbus结果实体
    /// </summary>
    /// <typeparam name="DateType"></typeparam>
    public class ModbusResultModel<DateType> : ModbusResultModel
    {
        public ModbusResultModel() : base()
        {
            ResultList = new List<DateType>();
        }

        /// <summary>
        /// 解析后的数据
        /// </summary>
        public List<DateType> ResultList { get; set; }
    }
    #endregion

    #region  Modbus 基类

    /// <summary>
    /// Modbus 基类
    /// </summary>
    public abstract class ModbusBase
    {
        /// <summary>
        /// 生成读取报文的公共方法
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始寄存器地址</param>
        /// <returns>返回报文(协议格式：站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] GenerateReadCommandBytes(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
        {
            //1.拼接报文:
            var sendCommand = new List<byte>
            {

                //协议格式：站地址+功能码+起始寄存器地址+寄存器数量
                //站地址
                devAddr,
                //功能码
                (byte)functionCode.GetHashCode(),
                //起始寄存器地址
                (byte)(startAddr / 256),
                (byte)(startAddr % 256),
                //寄存器数量
                (byte)(length / 256),
                (byte)(length % 256)
            };
            //CRC
            //byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
            //sendCommand.AddRange(crc);

            return sendCommand.ToArray();
        }

        /// <summary>
        /// 生成读取报文的 公共方法
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="data">定入数据</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">写入地址</param>
        /// <returns>返回报文(协议格式：站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] GenerateWriteCommandBytes(byte devAddr, ushort data, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
        {
            //1.拼接报文:
            var sendCommand = new List<byte>
            {

                //协议格式：站地址+功能码+起始寄存器地址+寄存器数量
                //站地址
                devAddr,
                //功能码
                (byte)functionCode.GetHashCode(),
                //写入地址
                (byte)(startAddr / 256),
                (byte)(startAddr % 256)
            };
            //写入数据
            var temp_bytes = BitConverter.GetBytes(data);
            if (BitConverter.IsLittleEndian)
            {
                //temp_bytes.Reverse();
                Array.Reverse(temp_bytes);
            }
            sendCommand.AddRange(temp_bytes);
            //CRC
            //byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
            //sendCommand.AddRange(crc);

            return sendCommand.ToArray();
        }

        /// <summary>
        /// 生成发送命令报文
        /// </summary>
        /// <param name="sendCommand"></param>
        /// <returns></returns>
        protected string generateSendCommandStr(byte[] sendCommand)
        {
            var sendCommandStr = string.Empty;
            foreach (var item in sendCommand)
            {
                sendCommandStr += Convert.ToString(item, 16) + " ";
            }
            return sendCommandStr;
        }

        /// <summary>
        /// 验证CRC
        /// </summary>
        /// <param name="value">要验证的数据</param>
        /// <returns></returns>
       

        /// <summary>
        /// CRC校验
        /// </summary>
        /// <param name="pucFrame">字节数组</param>
        /// <param name="usLen">验证长度</param>
        /// <returns>2个字节</returns>
       
    }

    /// <summary>
    /// Modbus 功能码
    /// </summary>
    public enum ModbusFunctionCode
    {
        /// <summary>
        /// 读取输出线圈
        /// </summary>
        [Description("读取输出线圈")]
        ReadOutCoil = 1,

        /// <summary>
        /// 读取输入线圈
        /// </summary>
        [Description("读取输入线圈")]
        ReadInputCoil = 2,

        /// <summary>
        /// 读取保持寄存器
        /// </summary>
        [Description("读取保持寄存器")]
        ReadRegister = 3,

        /// <summary>
        /// 读取输入寄存器
        /// </summary>
        [Description("读取输入寄存器")]
        ReadInputRegister = 4,

        /// <summary>
        /// (写入)预置单线圈
        /// </summary>
        [Description("(写入)预置单线圈")]
        WriteCoil = 5,

        /// <summary>
        /// (写入)预置单个寄存器
        /// </summary>
        [Description("(写入)预置单个寄存器")]
        WriteRegister = 6,

        /// <summary>
        /// (写入)预置多寄存器
        /// </summary>
        [Description("(写入)预置多寄存器")]
        WriteRegisterMultiple = 16,
    }
    #endregion

    #region RTU 串口基类 SerialPortBase 
    //Modbus 规定4个存储区
    // 区号     名称     读写        范围
    // 0区     输出线圈  可读可写    00001-09999
    // 1区     输入线圈  只读        10001-19999
    // 2区     输入寄存器  只读      30001-39999
    // 4区     保存寄存器  可读可写  40001-19999

    //功能码
    //01H     读取输出线圈
    //02H     读取输入线圈
    //03H     读取保持寄存器
    //04H     读取输入寄存器
    //05H     （写入）预置单线圈
    //06H     （写入）预置寄存器
    //0FH     （写入）预置多线圈
    //10H     （写入）预置多寄存器

   
    #endregion

    #region Modbus 串口通讯

    /// <summary>
    /// Modbus 串口通讯
    /// </summary>
    public class ModbusRTU : SerialPortBase
    {

        /// <summary>
        /// Modbus 串口通讯
        /// </summary>
        /// <param name="portName">COM口名称</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">检验位</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        public ModbusRTU(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            Init(portName, baudRate, parity, dataBits, stopBits);
            //SerialPortObj.DataReceived += new SerialDataReceivedEventHandler(ComDataReceived);
        }

        /// <summary>
        /// 读取线圈数据 ok
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始寄存器地址</param>
        /// <returns>线圈数据</returns>
        public ModbusResultModel ReadData(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
        {
            return ReadData(devAddr, length, (byte)functionCode, startAddr);
        }

        /// <summary>
        /// 读取数据 ok
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始寄存器地址</param>
        /// <returns>线圈数据</returns>
        public ModbusResultModel ReadData(byte devAddr, ushort length, byte functionCode = 2, ushort startAddr = 0)
        {
            var resultModel = new ModbusResultModel();
            ////byte[] datas = null;
            //if (functionCode >= 1 && functionCode <= 4)
            //{
            //    try
            //    {
            //        //1.拼接报文:
            //        var sendCommand = new List<byte>();

            //        //协议格式：站地址+功能码+起始寄存器地址+寄存器数量+CRC
            //        //站地址
            //        sendCommand.Add(devAddr);
            //        //功能码
            //        sendCommand.Add(functionCode);
            //        //起始寄存器地址
            //        sendCommand.Add((byte)(startAddr / 256));
            //        sendCommand.Add((byte)(startAddr % 256));
            //        //寄存器数量
            //        sendCommand.Add((byte)(length / 256));
            //        sendCommand.Add((byte)(length % 256));
            //        //CRC
            //        byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
            //        sendCommand.AddRange(crc);

            //        resultModel.SendDataStr = generateSendCommandStr(sendCommand.ToArray());
            //        //2.发送报文
            //        SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Count);
            //        //3.接收报文
            //        Thread.Sleep(50);//要延时一下，才能读到数据

            //        //读取响应报文
            //        byte[] respBytes = new byte[SerialPortObj.BytesToRead];
            //        SerialPortObj.Read(respBytes, 0, respBytes.Length);
            //        // respBytes -> 01 01 02 00 00 B9 FC
            //        resultModel.Datas = respBytes;
            //        // 检查一个校验位
            //        //if (CheckCRC(respBytes) && (respBytes.Length == 5 + length * 2) 
            //        //    && respBytes[0] == devAdd && respBytes[1] == functionCode && respBytes[1] == length * 2)
            //        if (CheckCRC(respBytes) && respBytes[0] == devAddr && respBytes[1] == functionCode)
            //        {
            //            //datas = respBytes;
            //            resultModel.IsSucceed = true;
            //        }
            //        else
            //        {
            //            resultModel.Msg = "响应报文校验失败";
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        resultModel.Msg = "异常:" + ex.Message;
            //    }
            //}
            //else
            //{
            //    //throw new Exception("功能码不正确[1-4]");
            //    resultModel.Msg = "功能码不正确[1-4]";
            //}
            ////SerialPortObj.Close();
            return resultModel;
        }

        /// <summary>
        /// 写入单个寄存器 ok
        /// 数据示例: 200
        /// 功能码 6
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="value">写入的数据</param>
        /// <param name="startAddr">写入地址</param>
        /// <returns>是否成功</returns>
        public ModbusResultModel WriteDataShort(int devAddr, short value, short startAddr = 0)
        {
            var resultModel = new ModbusResultModel();
            //try
            //{
            //    //bool isOk = false;
            //    //1.拼接报文:
            //    //var sendCommand = GetSingleDataWriteMessage(devAdd, startAddr, value); //ok
            //    var sendCommand = GetSingleDataWriteMessageList(devAddr, startAddr, value); //ok
            //                                                                                //var sendCommandStr = string.Join(' ', sendCommand.ToArray());
            //    resultModel.SendDataStr = generateSendCommandStr(sendCommand);

            //    //2.发送报文
            //    SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Length);
            //    //3.接收报文
            //    Thread.Sleep(50);//要延时一下，才能读到数据

            //    //读取响应报文
            //    byte[] respBytes = new byte[SerialPortObj.BytesToRead];
            //    SerialPortObj.Read(respBytes, 0, respBytes.Length);
            //    // respBytes -> 01 01 02 00 00 B9 FC
            //    // 检查一个校验位
            //    if (CheckCRC(respBytes) && respBytes[0] == devAddr && respBytes[1] == 0x06)
            //    {
            //        //isOk = true;
            //        resultModel.IsSucceed = true;
            //    }
            //    else
            //    {
            //        resultModel.Msg = "响应报文校验失败";
            //    }
            //}
            //catch (Exception ex)
            //{
            //    resultModel.Msg = "异常:" + ex.Message;
            //}
            ////SerialPortObj.Close();
            return resultModel;
        }

        /// <summary>
        /// 写入单个寄存器 ok
        /// 数据示例: 200
        /// 功能码 6
        /// </summary>
        /// <param name="devAddr">站地址</param>
        /// <param name="dataList">写入的数据集合</param>
        /// <param name="startAddr">写入地址</param>
        /// <returns>是否成功</returns>
        public ModbusResultModel WriteDataShort(int devAddr, List<short> dataList, short startAddr = 0)
        {
            var resultModel = new ModbusResultModel();
            if (dataList != null && dataList.Count > 0)
            {
                foreach (var item in dataList)
                {
                    resultModel = WriteDataShort(devAddr, item, startAddr);
                    startAddr++;
                }
            }
            return resultModel;
        }

        /// <summary>
        /// 获取写入单个寄存器的报文
        /// </summary>
        /// <param name="slaveStation">从站地址</param>
        /// <param name="startAddr">寄存器地址</param>
        /// <param name="value">写入值</param>
        /// <returns>写入单个寄存器的报文</returns>
        private byte[] GetSingleDataWriteMessage(int slaveStation, short startAddr, short value)
        {
            //从站地址
            byte station = (byte)slaveStation;
            //功能码
            byte type = 0x06;//06H     （写入）预置寄存器
                             //寄存器地址
            byte[] start = BitConverter.GetBytes(startAddr);
            //值
            byte[] valueBytes = BitConverter.GetBytes(value);
            //根据计算机大小端存储方式进行高低字节转换
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(start);
                Array.Reverse(valueBytes);
            }
            //拼接报文
            byte[] result = new byte[] { station, type };
            result = result.Concat(start.Concat(valueBytes).ToArray()).ToArray();

            //计算校验码并拼接，返回最后的报文结果
            return result.Concat(Crc16(result, result.Length)).ToArray();
        }

        /// <summary>
        /// 获取写入单个寄存器的报文
        /// </summary>
        /// <param name="slaveStation">从站地址</param>
        /// <param name="startAddr">寄存器地址</param>
        /// <param name="data">写入值</param>
        /// <returns>写入单个寄存器的报文</returns>
        private byte[] GetSingleDataWriteMessageList(int slaveStation, short startAddr, short data)
        {
            //1.拼接报文:
            var sendCommand = new List<byte>();
            //从站地址
            byte station = (byte)slaveStation;
            //功能码
            byte type = 0x06;//06H     （写入）预置寄存器            
                             //寄存器地址
            byte[] start = BitConverter.GetBytes(startAddr);
            //值
            byte[] valueBytes = BitConverter.GetBytes(data);
            //根据计算机大小端存储方式进行高低字节转换
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(start);
                Array.Reverse(valueBytes);
            }
            sendCommand.Add((byte)slaveStation);
            sendCommand.Add(type);
            sendCommand.AddRange(start);
            sendCommand.AddRange(valueBytes);

            byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
            sendCommand.AddRange(crc);
            return sendCommand.ToArray();
        }

        /// <summary>
        /// 写入多个寄存器 ok
        /// 数据示例: 123.45f, 14.3f
        /// 功能码 10 
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="data">写入的数据</param>
        /// <param name="startAddr">写入地址</param>
        /// <returns>是否成功</returns>
        public ModbusResultModel WriteDataFloat(byte devAddr, float data, ushort startAddr = 0)
        {
            return WriteDataFloat(devAddr, new List<float>() { data }, startAddr);
        }

        /// <summary>
        /// 写入多个寄存器 ok
        /// 数据示例: 123.45f, 14.3f
        /// 功能码 10 
        /// </summary>
        /// <param name="devAdd">从站地址</param>
        /// <param name="dataList">写入的数据</param>
        /// <param name="startAddr">写入地址</param>
        /// <returns>是否成功</returns>
        public ModbusResultModel WriteDataFloat(byte devAddr, List<float> dataList, ushort startAddr = 0)
        {
            var resultModel = new ModbusResultModel();
            //if (dataList != null && dataList.Count > 0)
            //{
            //    try
            //    {
            //        byte functionCode = (byte)ModbusFunctionCode.WriteRegisterMultiple.GetHashCode();
            //        int length = dataList.Count * 2;
            //        //1.拼接报文:
            //        var sendCommand = new List<byte>();

            //        //协议格式：站地址+功能码+起始寄存器地址+寄存器数量+CRC
            //        //站地址
            //        sendCommand.Add(devAddr);
            //        //功能码
            //        sendCommand.Add(functionCode);
            //        //写入地址
            //        sendCommand.Add((byte)(startAddr / 256));
            //        sendCommand.Add((byte)(startAddr % 256));
            //        //寄存器数量
            //        sendCommand.Add((byte)(length / 256));
            //        sendCommand.Add((byte)(length % 256));
            //        // 获取数值的byte[]
            //        List<byte> valueBytes = new List<byte>();
            //        foreach (var data in dataList)
            //        {
            //            List<byte> temp = new List<byte>(BitConverter.GetBytes(data));
            //            temp.Reverse();// 调整字节序
            //            valueBytes.AddRange(temp);
            //        }
            //        // 字节数
            //        sendCommand.Add((byte)valueBytes.Count);
            //        sendCommand.AddRange(valueBytes);
            //        //CRC
            //        byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
            //        sendCommand.AddRange(crc);

            //        //000004 - Rx:01 10 00 02 00 04 08 42 F6 E6 66 41 64 CC CD 83 23
            //        //000005 - Tx:01 10 00 02 00 04 60 0A
            //        //000006 - Rx:01 0A 00 02 00 04 08 42 F6 E6 66 41 64 CC CD 98 F9
            //        //000007 - Tx:01 8A 01 86 A0 //报错了

            //        //2.发送报文
            //        SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Count);
            //        //3.接收报文
            //        Thread.Sleep(50);//要延时一下，才能读到数据

            //        //读取响应报文
            //        byte[] respBytes = new byte[SerialPortObj.BytesToRead];
            //        SerialPortObj.Read(respBytes, 0, respBytes.Length);
            //        // respBytes -> 01 01 02 00 00 B9 FC

            //        // 检查一个校验位
            //        if (CheckCRC(respBytes) && respBytes[0] == devAddr && respBytes[1] == functionCode)
            //        {
            //            resultModel.IsSucceed = true;
            //        }
            //        else
            //        {
            //            resultModel.Msg = "响应报文校验失败";
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        resultModel.Msg = "异常:" + ex.Message;
            //    }
            //    //SerialPortObj.Close();
            //}
            //else
            //{
            //    resultModel.Msg = "dataLis参数不能为NULL 且 Count 要大于0";
            //}
            return resultModel;
        }

        /// <summary>
        /// 写单个线圈输出 ok
        /// </summary>
        /// <param name="on">开关</param>
        /// <param name="devAddr">从站地址</param>
        /// <param name="startAddr">写入地址</param>
        /// <returns></returns>
        public ModbusResultModel WriteSingleOutOnOff(bool on, byte devAddr = 1, ushort startAddr = 0)
        {
            var resultModel = new ModbusResultModel();
            try
            {
                //var isOk = false;
                //1.拼接报文:
                var sendCommand = new List<byte>
                {
                    //协议格式：站地址+功能码+起始寄存器地址+寄存器数量+CRC
                    //站地址
                    devAddr
                };
                //功能码
                byte functionCode = 0x05;
                sendCommand.Add(functionCode);
                //写入地址
                sendCommand.Add((byte)(startAddr / 256));
                sendCommand.Add((byte)(startAddr % 256));
                //写入数据
                sendCommand.Add((byte)(on ? 0xFF : 0x00));//true : 0xFF 开，false : 0x00 关
                sendCommand.Add(0x00);
                //CRC
                byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
                sendCommand.AddRange(crc);
                //2.发送报文
                //SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Count);
                //isOk = true;
                resultModel.IsSucceed = true;
            }
            catch (Exception ex)
            {
                resultModel.Msg = "异常:" + ex.Message;
            }
            return resultModel;
        }
    }
    #endregion
    #region //Modbus 串口通讯 读线圈状态(这个类是针对线圈的突出读取数据）  

    /// <summary>
    /// Modbus 串口通讯 读线圈状态
    /// </summary>
    public class ModbusRTUCoil : ModbusRTU
    {
        //ModbusRTU rtu = new ModbusRTU(portName);
        //var resultModel = rtu.ReadData(1, readLen, ModbusFunctionCode.ReadOutCoil);

        /// <summary>
        /// Modbus 串口通讯
        /// </summary>
        /// <param name="portName">COM口名称</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">检验位</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        public ModbusRTUCoil(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
            : base(portName, baudRate, parity, dataBits, stopBits)
        {
            //Init(portName, baudRate, parity, dataBits, stopBits);
            //SerialPortObj.DataReceived += new SerialDataReceivedEventHandler(ComDataReceived);
        }

        /// <summary>
        /// 读取线圈数据 ok
        /// </summary>
        /// <param name="devAdd">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始寄存器地址</param>
        /// <returns>线圈数据</returns>
        public ModbusResultModel<bool> ReadDataCoil(byte devAdd, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
        {
            var resultModel = new ModbusResultModel<bool>();
            var model = ReadData(devAdd, length, (byte)functionCode, startAddr);
            if (model != null && model.Datas != null && model.Datas.Length > 5)
            {
                resultModel.IsSucceed = model.IsSucceed;
                //报文解析
                // 检查一个校验位
                List<byte> respList = new List<byte>(model.Datas);
                respList.RemoveRange(0, 3);
                respList.RemoveRange(respList.Count - 2, 2);
                // 00 00
                //集合反转
                respList.Reverse();
                //转换成2进制
                var respStrList = respList.Select(r => Convert.ToString(r, 2)).ToList();
                var values = string.Join("", respStrList).ToList();
                values.Reverse();
                //values.ForEach(c => Console.WriteLine(Convert.ToBoolean(int.Parse(c.ToString()))));
                foreach (var v in values)
                {
                    resultModel.ResultList.Add(v.ToString() == "1");
                }
            }
            return resultModel;
        }
    }
    #endregion
    #region ModbusTCP 基类
    /// <summary>
    /// ModbusTCP 基类
    /// </summary>
    public abstract class ModbusTCPBase : ModbusBase
    {
        private Socket _socket = null;
        ushort _tid = 0;//TransactionId 最大 65535

        /// <summary>
        /// 异常码 字典
        /// </summary>
        protected Dictionary<int, string> Errors = new Dictionary<int, string>() {
            { 0x01 , "非法功能码"},
            { 0x02 , "非法数据地址"},
            { 0x03 , "非法数据值"},
            { 0x04 , "从站设备故障"},
            { 0x05 , "确认，从站需要一个耗时操作"},
            { 0x06 , "从站忙"},
            { 0x08 , "存储奇偶性差错"},
            { 0x0A , "不可用网关路径"},
            { 0x0B , "网关目标设备响应失败"},
        };

        /// <summary>
        /// Modbus TCP 通讯 初始化
        /// </summary>
        /// <param name="host">主机地址</param>
        /// <param name="port">端口</param>
        protected void Init(string host, int port)
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.Connect(host, port);
        }

        /// <summary>
        /// 读取报文的 公共方法
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始寄存器地址</param>
        /// <returns>返回报文(协议格式：TransactionId+协议标识+后续字节数+站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] GenerateTcpCommandReadBytes(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
        {
            var baseCommand = GenerateReadCommandBytes(devAddr, length, functionCode, startAddr);

            var sendCommand = new List<byte>
            {
                //TransactionId
                (byte)(_tid / 256),
                (byte)(_tid % 256),
                //Modbus 协议标识
                0x00,
                0x00,
                //后续字节数
                (byte)(baseCommand.Length / 256),
                (byte)(baseCommand.Length % 256)
            };
            _tid++;
            _tid %= 65535;
            sendCommand.AddRange(baseCommand);
            return sendCommand.ToArray();
        }

        /// <summary>
        /// 读取报文的 公共方法
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="data">输入数据</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始寄存器地址</param>
        /// <returns>返回报文(协议格式：TransactionId+协议标识+后续字节数+站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] GenerateTcpCommandWriteBytes(byte devAddr, ushort data, ModbusFunctionCode functionCode = ModbusFunctionCode.WriteRegister, ushort startAddr = 0)
        {
            var baseCommand = GenerateWriteCommandBytes(devAddr, data, functionCode, startAddr);

            var sendCommand = new List<byte>
            {
                //TransactionId
                (byte)(_tid / 256),
                (byte)(_tid % 256),
                //Modbus 协议标识
                0x00,
                0x00,
                //后续字节数
                (byte)(baseCommand.Length / 256),
                (byte)(baseCommand.Length % 256)
            };
            _tid++;
            _tid %= 65535;
            sendCommand.AddRange(baseCommand);
            return sendCommand.ToArray();
        }

        protected ModbusResultModel SendCommand(byte[] sendCommand)
        {
            var resultModel = new ModbusResultModel();
            try
            {
                //报文
                //TransactionId Modbus 协议标识 后续字节数  从站地址  功能码         起始寄存器地址  寄存器数量  CRC
                //0x00 0x01     0x00   0x00     0x00  0x06  devAddr   functionCode   0x00  0x0A      

                resultModel.SendDataStr = generateSendCommandStr(sendCommand.ToArray());
                _socket.Send(sendCommand.ToArray());
                //000002-Rx:00 00 00 00 00 06 01 03 00 01 00 05
                //000003-Tx:00 00 00 00 00 0D 01 03 0A 00 00 00 00 00 00 00 00 00 00
                // 00 00 00 00 00 0D 前6位
                // 01 03 0A (0,1,2)
                // 0A 数据长度 （0A=10）

                //先取前6位，固定返回
                var resp_bytes = new byte[6];// 00 00 00 00 00 0D 前6位
                _socket.Receive(resp_bytes, 0, resp_bytes.Length, SocketFlags.None);

                //取出下标为 ：4和5的数据[00 0D]
                var len_bytes = resp_bytes.ToList().GetRange(4, 2);
                //起始寄存器地址 的反向操作
                //将下标为4和5 两个字节转成10进制数
                int len = len_bytes[0] * 256 + len_bytes[1];

                //获取数据的长度
                //01 03 0A 00 00 00 00 00 00 00 00 00 00 [正常]
                //01 83 02 [异常，83， 异常代码 ：02]
                resp_bytes = new byte[len];
                _socket.Receive(resp_bytes, 0, len, SocketFlags.None);

                //检查响应报文是否正常
                //0x83 1000 0011
                //01 83 02 [异常，83， 异常代码 ：02]
                if (resp_bytes[1] > 0x08)//判断是否异常
                {
                    //resp_bytes[2] = 异常代码 ：02
                    //说明响应是异常报文
                    //返回异常信息，根据resp_bytes字节进行异常关联
                    if (Errors.ContainsKey(resp_bytes[2]))
                    {
                        resultModel.Msg = Errors[resp_bytes[2]];//获取异常码对应的异常说明
                    }
                }
                else
                {
                    //resp_bytes[2] = 0A 数据长度 （0A=10）
                    //正常
                    resultModel.Datas = resp_bytes.ToList().GetRange(3, resp_bytes[2]).ToArray();
                    resultModel.IsSucceed = true;
                }
            }
            catch (Exception ex)
            {
                resultModel.Msg = ex.Message;
            }
            return resultModel;
        }

        /// <summary>
        /// 解析数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="datas"></param>
        /// <returns></returns>
        public List<T> AnalysisDatas<T>(byte[] datas)
        {
            //data_bytes 每两个字节转成一个数字， float 4个字节转成一个数字，double 8个字节转成一个数字
            //2  ushort short int16 uint32 float
            //4  int uint int32 uint32 float
            //8  double
            //16 decimal
            var resultValue = new List<T>();
            try
            {
                var type_len = Marshal.SizeOf(typeof(T));
                for (int i = 0; i < datas.Length; i += type_len)
                {
                    var temp_bytes = datas.ToList().GetRange(i, type_len);
                    if (BitConverter.IsLittleEndian)
                    {
                        temp_bytes.Reverse();
                    }
                    //反射 方法
                    Type bitConverter_type = typeof(BitConverter);
                    var typeMethodList = bitConverter_type.GetMethods().ToList();
                    //找到返回类型和传入的类型一至，且方法的参数是2个的方法
                    var method = typeMethodList.FirstOrDefault(mi => mi.ReturnType == typeof(T)
                                        && mi.GetParameters().Length == 2);
                    if (method == null)
                    {
                        throw new Exception("数据转换类型出错!");
                    }
                    else
                    {
                        //由 bitConverter_type 执行找到的 method方法，注意参数数量，上面找是的两个参数的方法
                        var value = method.Invoke(bitConverter_type, new object[] { temp_bytes.ToArray(), 0 });
                        resultValue.Add((T)value);
                    }
                }
            }
            catch
            {

            }
            return resultValue;
        }
    }
    #endregion


    #region //Modbus TCP 通讯

    /// <summary>
    /// Modbus TCP 通讯
    /// </summary>
    public class ModbusTCP : ModbusTCPBase
    {

        /// <summary>
        /// Modbus TCP 通讯
        /// </summary>
        /// <param name="host">主机地址</param>
        /// <param name="port">端口</param>
        public ModbusTCP(string host, int port)
        {
            //_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //_socket.Connect(host, port);
            Init(host, port);
        }

        /// <summary>
        /// 读取保持型寄存器 03
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="count">数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始地址</param>
        //public ModbusResultModel ReadHoldingRegister(byte devAddr, ushort length, byte functionCode = 3, ushort startAddr = 0)
        //{
        //    var resultModel = new ModbusResultModel();
        //    //报文
        //    //TransactionId Modbus 协议标识 后续字节数  从站地址  功能码         起始寄存器地址  寄存器数量  CRC
        //    //0x00 0x01     0x00   0x00     0x00  0x06  devAddr   functionCode   0x00  0x0A      
        //    try
        //    {
        //        ushort tid = 0;//TransactionId 最大 65535

        //        var sendCommand = new List<byte>();
        //        //TransactionId
        //        sendCommand.Add((byte)(tid / 256));
        //        sendCommand.Add((byte)(tid % 256));
        //        //Modbus 协议标识
        //        sendCommand.Add(0x00);
        //        sendCommand.Add(0x00);
        //        //后续字节数
        //        sendCommand.Add(0x00);
        //        sendCommand.Add(0x06);
        //        //从站地址
        //        sendCommand.Add(devAddr);
        //        //功能码
        //        sendCommand.Add(functionCode);
        //        //起始寄存器地址
        //        sendCommand.Add((byte)(startAddr / 256));
        //        sendCommand.Add((byte)(startAddr % 256));
        //        //寄存器数量
        //        sendCommand.Add((byte)(length / 256));
        //        sendCommand.Add((byte)(length % 256));
        //        //CRC
        //        //byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
        //        //sendCommand.AddRange(crc);
        //        tid++;
        //        tid %= 65535;
        //        resultModel.SendDataStr = generateSendCommandStr(sendCommand.ToArray());
        //        _socket.Send(sendCommand.ToArray());
        //        //000002-Rx:00 00 00 00 00 06 01 03 00 01 00 05
        //        //000003-Tx:00 00 00 00 00 0D 01 03 0A 00 00 00 00 00 00 00 00 00 00
        //        // 00 00 00 00 00 0D 前6位
        //        // 01 03 0A (0,1,2)
        //        // 0A 数据长度 （0A=10）

        //        //先取前6位，固定返回
        //        var resp_bytes = new byte[6];// 00 00 00 00 00 0D 前6位
        //        _socket.Receive(resp_bytes, 0, resp_bytes.Length, SocketFlags.None);

        //        //取出下标为 ：4和5的数据[00 0D]
        //        var len_bytes = resp_bytes.ToList().GetRange(4, 2);
        //        //起始寄存器地址 的反向操作
        //        //将下标为4和5 两个字节转成10进制数
        //        int len = resp_bytes[4] * 256 + resp_bytes[5];

        //        //获取数据的长度
        //        //01 03 0A 00 00 00 00 00 00 00 00 00 00 [正常]
        //        //01 83 02 [异常，83， 异常代码 ：02]
        //        resp_bytes = new byte[len];
        //        _socket.Receive(resp_bytes, 0, len, SocketFlags.None);

        //        //检查响应报文是否正常
        //        //0x83 1000 0011
        //        //01 83 02 [异常，83， 异常代码 ：02]
        //        if (resp_bytes[1] > 0x08)//判断是否异常
        //        {
        //            //resp_bytes[2] = 异常代码 ：02
        //            //说明响应是异常报文
        //            //返回异常信息，根据resp_bytes字节进行异常关联
        //            if (Errors.ContainsKey(resp_bytes[2]))
        //            {
        //                resultModel.Msg = Errors[resp_bytes[2]];//获取异常码对应的异常说明
        //            }
        //        }
        //        else
        //        {
        //            //resp_bytes[2] = 0A 数据长度 （0A=10）
        //            //正常
        //            resultModel.Datas = resp_bytes.ToList().GetRange(3, resp_bytes[2]).ToArray();
        //            resultModel.IsSucceed = true;
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        resultModel.Msg = ex.Message;
        //    }
        //    return resultModel;
        //}

        /// <summary>
        /// 读取保持型寄存器 03
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="length">数量</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始地址</param>
        /// <returns>返回对象</returns>
        public ModbusResultModel<T> ReadHoldingRegister<T>(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
        {
            var resultModel = new ModbusResultModel<T>();
            try
            {
                var command = GenerateTcpCommandReadBytes(devAddr, length, functionCode, startAddr);

                resultModel.SendDataStr = generateSendCommandStr(command.ToArray());
                var receptionModel = SendCommand(command.ToArray());
                if (receptionModel.IsSucceed
                    && receptionModel.Datas != null && receptionModel.Datas.Length > 0)
                {
                    resultModel.Datas = receptionModel.Datas;
                    resultModel.ResultList = AnalysisDatas<T>(receptionModel.Datas);
                    resultModel.IsSucceed = true;
                }
            }
            catch (Exception ex)
            {
                resultModel.Msg = ex.Message;
            }
            return resultModel;
        }

        /// <summary>
        /// 写入保持型寄存器 03
        /// </summary>
        /// <param name="devAddr">从站地址</param>
        /// <param name="datas">写入数据</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="startAddr">起始地址</param>
        /// <returns>返回对象</returns>
        public ModbusResultModel WriteHoldingRegister(byte devAddr, ushort data, ModbusFunctionCode functionCode = ModbusFunctionCode.WriteRegister, ushort startAddr = 0)
        {
            var resultModel = new ModbusResultModel();
            try
            {
                var command = GenerateTcpCommandWriteBytes(devAddr, data, functionCode, startAddr);

                resultModel.SendDataStr = generateSendCommandStr(command.ToArray());
                var receptionModel = SendCommand(command.ToArray());
                if (receptionModel.IsSucceed)
                {
                    resultModel.IsSucceed = true;
                }
            }
            catch (Exception ex)
            {
                resultModel.Msg = ex.Message;
            }
            return resultModel;
        }
    }
    #endregion
}
